function checkStatus(response) {
	if (response.status >= 200 && response.status < 300) {
		return response
	} else {
		var error = new Error(response.statusText);
		error.response = response;
		throw error
	}
}

function parseJSON(response) {
	return response.json()
}

function checkResponse(response) {
	if (response.success) {
		return response.data;
	}
	else {
		var error = new Error(response.message);
		error.message = response.message;
		throw error
	}
}

class Api {

	constructor() {
		this.apiGenerator("Note");
		this.apiGenerator("Book");
		this.putProfile = ({avatar, nickname, gender, signature}) => this.putRequest("/api/user/profile", {
			avatar,
			nickname,
			gender,
			signature
		});

		this.getProfile = () => this.getRequest("/api/user/profile");

		this.putResetPassword = ({oldPassword, newPassword}) =>this.putRequest("/api/user/password", {
			oldPassword,
			newPassword
		});

		this.getTestPassword = ({password}) =>this.getRequest(`/api/user/password?password=${password}`);
	}

	apiGenerator(model) {
		this[`list${model}`] = function () {
			return this.getRequest(`/api/${model.toLowerCase()}`);
		};
		this[`post${model}`] = function (data) {
			return this.postRequest(`/api/${model.toLowerCase()}`, data);
		};
		this[`get${model}`] = function (_id) {
			return this.getRequest(`/api/${model.toLowerCase()}/${_id}`);
		};
		this[`put${model}`] = function (_id, data) {
			return this.putRequest(`/api/${model.toLowerCase()}/${_id}`, data);
		};
		this[`remove${model}`] = function (_id) {
			return this.deleteRequest(`/api/${model.toLowerCase()}/${_id}`);
		};
		return this;
	}

	getRequest(url, args) {
		return fetch(url, {
			credentials: 'same-origin',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			},
			body: args,
		}).then(checkStatus)
			.then(parseJSON)
			.then(checkResponse);
	}

	postRequest(url, data) {
		return fetch(url, {
			method: 'POST',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			},
			credentials: 'same-origin',
			body: JSON.stringify(data),
		}).then(checkStatus)
			.then(parseJSON)
			.then(checkResponse);
	}

	putRequest(url, data) {
		return fetch(url, {
			method: 'PUT',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			},
			credentials: 'same-origin',
			body: JSON.stringify(data),
		}).then(checkStatus)
			.then(parseJSON)
			.then(checkResponse);
	}

	deleteRequest(url, data) {
		return fetch(url, {
			method: 'DELETE',
			headers: {
				'Accept': 'application/json',
				'Content-Type': 'application/json'
			},
			credentials: 'same-origin',
			body: JSON.stringify(data),
		}).then(checkStatus)
			.then(parseJSON)
			.then(checkResponse);
	}
}

let api = new Api();

class Model {
	static baseURL = '';
	static _idKey = '_id';
	_attributes = {};
	__listeners = {};

	constructor(initAttrs) {
		if (_.isObject(initAttrs)) {
			this.set(initAttrs);
		} else if (_.isString(initAttrs)) {
			this.set({_id: initAttrs});
		}
	}

	get(name) {
		return this._attributes[name];
	}

	get id() {
		return this.constructor.staticIdKey || this._attributes[this.constructor._idKey];
	}

	get url() {
		return this.id ? `${this.constructor.baseURL}/${this.id}` : this.constructor.baseURL;
	}

	get saveMethod() {
		return this.id ? 'PUT' : 'POST'
	}

	set(initAttrs) {
		this._attributes = Object.assign(this._attributes, initAttrs);
		this.trigger('set', this)
	}

	reset() {
		this.trigger('reset', this)
	}

	fetch() {
		return api.getRequest(this.url).then(this.set.bind(this));
	}

	save() {
		return this.id ? api.putRequest(this.url, this._attributes) : api.postRequest(this.url, this._attributes);
	}

	remove() {
		if (!this.id) throw Error("没有该对象不能删除");
		return api.deleteRequest(this.url);
	}

	on(event, fn) {
		this.__listeners[event] = this.__listeners[event] || [];
		this.__listeners[event].push(fn);
	}

	off(event, fn) {
		this.__listeners[event] = this.__listeners[event] || [];
		this.__listeners[event] = this.__listeners[event].filter((l) => l != fn);
	}

	one(event, fn) {
		let flash = (...args) => {
			fn(...args);
			this.off(event, flash);
		};
		this.on(event, flash)
	}

	trigger(event, ...args) {
		this.__listeners[event] = this.__listeners[event] || [];
		this.__listeners[event].forEach((fn) => fn(...args));
	}

	toJSON() {
		return this._attributes;
	}
}

class Collection {

	static baseURL = '';
	static BaseModel = Model;
	page = 1;
	size = 20;
	total = 0;

}

class Book extends Model {
	static baseURL = '/api/book'
}
class Note extends Model {
	static baseURL = '/api/note'
}

export class SessionUser extends Model {
	static baseURL = '/api/user';
	static staticIdKey = 'session';

	get username() {
		return this._attributes.nickname || `${this._attributes.first_name} ${this._attributes.last_name}`;
	}
}

export {
	Model,
	Collection
};
export default api;